package com.zte.claa.inficombo.csa.app.service.rptparse.ext;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.script.Invocable;

import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCmdBuilder;
import com.zte.claa.inficombo.csa.app.comm.csif.CSIFCommService;
import com.zte.claa.inficombo.csa.app.model.dev.DevBase;
import com.zte.claa.inficombo.csa.app.model.redis.AlarmData;
import com.zte.claa.inficombo.csa.app.service.jsplugin.JSParam;
import com.zte.claa.inficombo.csa.app.service.rptparse.AppCmdWrapper;
import com.zte.claa.inficombo.csa.app.service.rptparse.MultiPackageData;
import com.zte.claa.inficombo.csa.app.service.rptparse.ParserUpdata;
import com.zte.claa.inficombo.csa.app.util.JsonUtils;

public class ThirdPartyRawUpdataSubParser extends ParserUpdata implements UpDataSubParser {
	public static final String PARSER_TP_3RDP_RAW = "sp_3rd_raw_parser";
	
	// 日志记录.
	private Logger logger = LoggerFactory.getLogger(ThirdPartyRawUpdataSubParser.class);

	@Override
	public String getParserType() {
		return PARSER_TP_3RDP_RAW;
	}

	@Override
	public void parseUpData(JSONObject json, DevBase dev, CSIFCommService svr) throws Exception {
		int multiPkgFlag = this.parseUpRptData(json, dev, svr);
		// 非组包情况.
		if(multiPkgFlag == 0){
			String payload = JsonUtils.getJsonAttrVal(json, "payload", null);
			String portStr = JsonUtils.getJsonAttrVal(json, "port", "0");
			byte[] payloadBS = CSIFCmdBuilder.base64Decode(payload);
			ExtRptDataParser extParser = this.container.getExtRptDataDealer(dev.getDevType());
			if (extParser != null) {
				RptRawData rawData = new RptRawData();
				rawData.setAppeui(dev.getAppEui());
				rawData.setDeveui(dev.getDevEui());
				rawData.setDevName(dev.getDevName());
				rawData.setDevtype(dev.getDevType());
				rawData.setPayload(payloadBS);
				rawData.setProjectid(dev.getProjectId());
				rawData.setPortStr(portStr);
				rawData.setRptTime(System.currentTimeMillis());
				extParser.processRptData(rawData);
				logger.info("[1]Submit RptRawData ok: " + rawData);
			} else {
				logger.warn("Not find ExtRptDataDealer for devtype: "+dev.getDevType());
			}
		}
	} 
	
	/**
	 * 一、解析框架部分的主要逻辑：
	 * 1、如果当前MPD不空，则与返回结果合并(调用container.merge方法.)；
	 * 2、如果MPD.isDataOk=yes，isParsed=yes,confirm=yes，则此时JS已经做了metedata解析和ackcmd下发；MPD从缓存删除；调外部扩展接口；
	 * 3、如果MPD.isDataOk=yes，isParsed=yes,confirm=NO，  则此时JS已经做了metedata解析，框架侧走metedata解析流程；MPD入缓存；
	 * 4、如果MPD.isDataOk=yes，isParsed=no, confirm=NO，  则此时JS未做metedata解析，触发二次解析，设置isparsed=yes；MPD入缓存；
	 * 5、如果MPD.isDataOk=yes，isParsed=no, confirm=yes，则此时JS未做metedata解析，触发二次解析，设置isparsed=yes；MPD入缓存；
	 * 6、如果MPD.isDataOk=NO，confirm=yes，则此时JS已经做了ackcmd下发，框架侧走相应ACK流程；MPD入缓存；
	 * 7、如果MPD.isDataOk=NO，confirm=NO，此时MPD入缓存，等待后续包；
	 * 
	 * 二、相应地JS插件内部逻辑：（注意确认包序号是从1开始还是从0开始，JS-Framework约定从1开始）
	 * 1、接收原始byte[]流-bs，并尝试从JSParam中获取multipkgdata参数-mpd；
	 * 2、如果mpd参数为空，则创建一个mpd对象；
	 * 3、当前bs是普通分包数据包，则解析分包数据，确定包序号；
	 * 	   3.1 将包append到mpd中，注意：判断是否出现重复包，如果出现则将mpd重新初始化；
	 * 	   3.2 判断mpd是否dataok；
	 *     3.3 如果dataok，则调用buildData()解析出metedata（本次GNSS设备不需要，其他普通分包类型的情况需要），设置isparsed=yes；
	 *     3.4返回mpd及metedata（本次GNSS不需要），特别地，如果GNSS服务器只需要GNSS终端的净荷，还涉及data的特殊处理.
	 * 4、当前bs是分包上行校验包（分包发送完毕后的确认消息）；
	 *	   4.1 设置mpd的confirmed值=1；
	 *	   4.2 获取确认位图（getBitmap）及构造ackcmd，使用全1模式（ignoreabsentpkg=true）；
	 *	   4.3返回mpd和ackcmd；
	 * 5、新增二次解析方法入口： UPDATA_MULTIPKG_REPARSE_FUNC = "funcmultipkgreparse";
	 * 	   5.1框架针对分包的类型调用，入参是mpd；
	 *     5.2调用buildData()，得到完整码流，解析码流返回metedata（可选）和mpd（设置isparsed=yes）；
	 */
	protected void processMultiPkgData(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		if(mpd == null){
			return;
		}
		String devEui = dev.getDevEui();
		// 加锁互斥，避免重复处理.
		synchronized (devEui) {
			MultiPackageData mergeData = this.container.mergePackageData(mpd);
			if(mergeData == null){
				return;
			}
			boolean isConfirmed = (mergeData.getIsConfirmed() == MultiPackageData.CONFIRMED_YES);
			boolean isDataOk = mergeData.isDataOk();
			boolean isParsed = (mergeData.getIsParsed() == MultiPackageData.PARSED_YES);
			if (isDataOk && isParsed && isConfirmed) {
				this.container.removeMultiPkgData(mpd.getDeveui());
				// 调用外部扩展处理...1）解析，2）持久化，3）发metedata广播；
				ExtRptDataParser extParser = this.container.getExtRptDataDealer(mpd.getDevtype());
				if (extParser != null) {
					RptRawData rawData = new RptRawData();
					rawData.setAppeui(mpd.getAppeui());
					rawData.setDeveui(mpd.getDeveui());
					rawData.setDevName(mpd.getDevName());
					rawData.setDevtype(mpd.getDevtype());
					rawData.setPayload(mpd.buildData());
					rawData.setProjectid(mpd.getProjectid());
					rawData.setPortStr(mpd.getPortStr());
					rawData.setRptTime(mpd.getRptTime());
					extParser.processRptData(rawData);
					logger.info("[2]Submit RptRawData ok: " + rawData);
				}
			} else if (isDataOk && !isParsed) {
				// 首先设置解析标记.
				mergeData.setIsParsed(MultiPackageData.PARSED_YES);
				this.container.setMultiPkgData(mergeData);
				// 分包收集完整后进行二次解析
				this.reParseMultiPkgData(mergeData, jsRunner, dev, portStr);
				this.container.removeMultiPkgData(mpd.getDeveui());
				logger.info("reParseMultiPkgData: " + mergeData);
			} else if (isDataOk && isParsed && !isConfirmed) {
				this.container.setMultiPkgData(mergeData);
			} else if (!isDataOk && isConfirmed) {
				this.container.setMultiPkgData(mergeData);
			} else if (!isDataOk && !isConfirmed) {
				this.container.setMultiPkgData(mergeData);
			}
		}
	}

	/**
	 * 分包收集完整后进行补充二次解析
	 * 
	 * @param mpd
	 * @param jsRunner
	 */
	@SuppressWarnings("unchecked")
	protected void reParseMultiPkgData(MultiPackageData mpd, Invocable jsRunner, DevBase dev, String portStr) throws Exception {
		// 设置可能的扩展配置数据，供js插件访问.
		this.setExtDataToMultiPkg(mpd, dev);
		Object rptData = jsRunner.invokeFunction(JSParam.UPDATA_MULTIPKG_REPARSE_FUNC, mpd);
		// 情况1--解析结果无效；
		if(rptData == null){
			logger.info("[*!*]MPD-Reparse: Invalid or nouse report data!");
			return;
		}
		// 情况2--只有alarm数据；	
		else if(AlarmData.class.isInstance(rptData)){
			logger.debug("MPD-Reparse: Alarm data return from js plugin: "+rptData);
			this.parseAlarmData((AlarmData)rptData);
			return;
		}		
		// 情况3--只有metedata数据；
		else if(String.class.isInstance(rptData)){
			logger.debug("MPD-Reparse: Mete data return from js plugin: "+rptData.toString());
			this.parseMeteData(dev, rptData.toString(), portStr);
			return;
		}		
		// 情况4--同时有metedata、alarm数据、ackcmd（一种或几种兼有）；
		else if(HashMap.class.isInstance(rptData)){
			Map<String, Object> map = (HashMap<String, Object>)rptData;
			// 4.1单条告警...
			Object ao = map.get(UPD_KEY_ALARM);
			if (ao != null && AlarmData.class.isInstance(ao)) {
				logger.debug("Alarm data return from js plugin: " + ao);
				try {
					this.parseAlarmData((AlarmData) ao);
				} catch (Exception ex) {
					logger.error("parseAlarmData failed: " + ao, ex);
				}
			}
			// 4.2告警列表...
			Object aoList = map.get(UPD_KEY_ALARMLIST);
			if (aoList != null && ArrayList.class.isInstance(aoList)) {
				logger.debug("Alarm data list return from js plugin: " + aoList);
				ArrayList<AlarmData> alarmList = (ArrayList<AlarmData>) aoList;
				for (AlarmData alarm : alarmList) {
					try {
						this.parseAlarmData(alarm);
					} catch (Exception ex) {
						logger.error("parseAlarmData failed: " + alarm, ex);
					}
				}
			}		
			// 4.3单条mdata（仅限于模型字段的json字符串）...
			Object mo = map.get(UPD_KEY_METEDATA);
			if (mo != null && String.class.isInstance(mo)) {
				logger.debug("Mete data return from js plugin: " + mo);
				try {
					this.parseMeteData(dev, mo.toString(), portStr);
				} catch (Exception ex) {
					logger.error("parseMeteData failed: " + mo, ex);
				}
			}
			// 4.4mdata列表（仅限于模型字段的json字符串）...
			Object moList = map.get(UPD_KEY_METEDATALIST);
			if (moList != null && ArrayList.class.isInstance(moList)) {
				logger.debug("Mete data list return from js plugin: " + moList);
				ArrayList<String> mdataList = (ArrayList<String>) moList;
				this.parseMeteDataByBatch(dev, mdataList, portStr);
			}
			// 4.5个别设备需要回复响应确认...
			Object ackCmd = map.get(UPD_KEY_ACKCMD);
			if (ackCmd != null && AppCmdWrapper.class.isInstance(ackCmd)) {
				logger.debug("Ack command return from js plugin: " + ackCmd);
				try {
					AppCmdWrapper cmdWrapper = (AppCmdWrapper)ackCmd;
					// send cmd in async way...
					this.container.sendDevCtrlCmd(cmdWrapper);
				} catch (Exception ex) {
					logger.error("Send ack cmd failed: " + ackCmd, ex);
				}
			}
			// 4.6 个别涉及分包情况.
			Object multiPkgObj = map.get(UPD_KEY_MULTIPKG);
			if(multiPkgObj != null && MultiPackageData.class.isInstance(multiPkgObj)){
				logger.debug("Multipackage data return from js plugin: " + multiPkgObj);
				try {
					this.reProcessMultiPkgData((MultiPackageData)multiPkgObj);	
				} catch (Exception ex) {
					logger.error("Multipackage data process failed: " + multiPkgObj, ex);
				}
			}
		}
		// 单个分包情况.
		else if (MultiPackageData.class.isInstance(rptData)){
			logger.debug("Multi-package data return from js plugin: "+rptData.toString());
			this.reProcessMultiPkgData((MultiPackageData)rptData);		
		}
		else {
			logger.warn("[*!*]Result data is invalid from js-plugin for devtype: "+dev.getDevType()+", data="+rptData);
		}
	}
	
	/**
	 * 二次解析组包结果MPD的二次处理方法
	 * 
	 * @param mpd
	 * @throws Exception
	 */
	protected void reProcessMultiPkgData(MultiPackageData mpd) throws Exception {
		if (mpd == null) {
			return;
		}
		this.container.removeMultiPkgData(mpd.getDeveui());
		// 调用外部扩展处理...1）解析，2）持久化，3）发metedata广播；----》单独的线程池队列实现.
		ExtRptDataParser extParser = this.container.getExtRptDataDealer(mpd.getDevtype());
		if (extParser != null) {
			RptRawData rawData = new RptRawData();
			rawData.setAppeui(mpd.getAppeui());
			rawData.setDeveui(mpd.getDeveui());
			rawData.setDevName(mpd.getDevName());
			rawData.setDevtype(mpd.getDevtype());
			rawData.setPayload(mpd.buildData());
			rawData.setProjectid(mpd.getProjectid());
			rawData.setPortStr(mpd.getPortStr());
			rawData.setRptTime(mpd.getRptTime());
			extParser.processRptData(rawData);
			logger.info("[3]ReSubmit RptRawData ok: " + rawData);
		}
	}
}
